#!/usr/bin/env python3

# Copyright (c) 2005-2014 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
import re
from ...._util import Util
from ...._plugin import KernelAddonPlugin


class Plugin(KernelAddonPlugin):

    def patch_kernel(self, context):
        # set swappiness to 1
        fullfn = os.path.join(context.get_kernel_work_dir(), "mm/vmscan.c")
        Util.sed(fullfn, "int vm_swappiness = 60;", "int vm_swappiness = 1;", exceptionClass=self.PatchKernelError)

    def modify_kernel_config(self, context):
        ## basic ##############################################
        # context.select_by_symbol("PREEMPT_RT", "y")                                                               # FIXME: some graphics drivers does not compatible with it
        context.set_choice_by_path(["General setup", "Timers subsystem", "Timer tick handling"], "NO_HZ_FULL")

        context.select_by_symbol("CHECKPOINT_RESTORE", "y")

        # desktop related hardward support
        context.select_by_symbol("HIGH_RES_TIMERS", "y")
        context.set_choice_by_path(["Power management and ACPI options", "CPU Frequency scaling", "CPU Frequency scaling", "Default CPUFreq governor"], "CPU_FREQ_DEFAULT_GOV_SCHEDUTIL")

        ## swap ##############################################
        context.select_by_symbol("SWAP", "y")

        # we use zswap + swapfile
        context.select_by_symbol("ZSWAP", "y")
        context.select_by_symbol("ZSWAP_DEFAULT_ON", "n")                                                                                                                                                    # zswap should be enabled manually
        context.select_by_symbol("ZSWAP_SHRINKER_DEFAULT_ON", "y")
        context.set_choice_by_path(["Memory Management options", "Support for paging of anonymous memory (swap)", "Compressed cache for swap pages", "Default compressor"], "ZSWAP_COMPRESSOR_DEFAULT_ZSTD")        # should use best and slowest algorithm

        # we use zram if swapfile is not applicable
        context.select_by_symbol("ZRAM", "m")
        context.set_choice_by_path(["Device Drivers", "Block devices", "Compressed RAM block device support", "Default zram compressor"], "ZRAM_DEF_COMP_ZSTD")        # should use best and slowest algorithm
        context.select_by_symbol("ZRAM_BACKEND_ZSTD", "y")
        context.select_tree_by_symbol("ZRAM", "n")                                                                                                                     # disable all other functions, including other compress algorithms

        # serial device is under tty subsystem, not a good design
        context.select_by_symbol("SERIAL_8250_16550A_VARIANTS", "n")                                                    # enable it takes additional time at boot
        context.set_integer_by_symbol("SERIAL_8250_RUNTIME_UARTS", 0)                                                   # only support PNP serial device
        context.select_by_symbol("SERIAL_8250_EXTENDED", "n")
        context.select_tree_by_symbol("SERIAL_8250", "m,y", value_priority=context.ValuePriority.USER_POLICY)
        context.select_tree_by_symbol("SERIAL_8250_PCI", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## input ############################################
        context.select_by_symbol("INPUT", "y")
        context.select_by_symbol("KEYBOARD_ATKBD", "y")                                                                 # many laptops still use AT keyboard and, AT keyboard does not support PnP
        context.select_by_symbol("INPUT_KEYBOARD", "y")
        context.select_by_symbol("INPUT_MISC", "y")
        context.select_by_symbol("INPUT_UINPUT", "y")                                                                   # /dev/uinput is a very useful interface
        context.select_tree_by_symbol("INPUT_MISC", "n", value_priority=context.ValuePriority.USER_POLICY)
        context.select_tree_by_path(["Device Drivers", "Input device support"], "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # FIXME
        if context.get_kernel_version().startswith("5"):
            context.select_tree_by_path(["Device Drivers", "HID support"], "m,y", value_priority=context.ValuePriority.USER_POLICY)
        else:
            context.select_tree_by_symbol("HID_SUPPORT", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## sound ############################################
        context.select_by_symbol("SND_USB_6FIRE", "n", allow_invisible=True)                                            # no firmware, we think it's rare
        context.select_by_symbol("SND_HDSP", "n", allow_invisible=True)                                                 # no firmware, we think it's rare
        context.select_by_symbol("SND_HDSPM", "n", allow_invisible=True)                                                # no firmware, we think it's rare
        context.select_by_symbol("SND_CS46XX", "n", allow_invisible=True)                                               # no firmware, we think it's rare
        context.select_by_symbol("SND_MIXART", "n", allow_invisible=True)                                               # no firmware, we think it's rare
        context.select_by_symbol("SND_ASIHPI", "n", allow_invisible=True)                                               # no firmware, we think it's rare
        context.select_by_symbol("SND_PCXHR", "n", allow_invisible=True)                                                # no firmware, we think it's rare
        for sym_name, path_seq in context.list_symbols_in_tree_by_path(["Device Drivers", "Sound card support", "Advanced Linux Sound Architecture", "PCI sound devices"], with_path=True):
            if re.search("\\(Echoaudio\\) .*", path_seq[-1]) is not None:
                context.select_by_symbol(sym_name, "n", allow_invisible=True)

        # context.select_by_symbol("SOUND", "m")
        context.select_by_symbol("SND_SOC", "n", allow_invisible=True)
        context.select_by_symbol("SND_PCSP", "n", allow_invisible=True)
        context.select_by_symbol("SND_SEQ_DUMMY", "n", allow_invisible=True)
        context.select_tree_by_symbol("SND_DRIVERS", "n", allow_invisible=True)                                         # disable all generic sound drivers, which are only used for testing purpose
        context.select_tree_by_symbol("SOUND", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## usb ##############################################
        context.select_by_symbol("MUSB_PIO_ONLY", "n", allow_invisible=True)
        context.select_by_symbol("USB_XHCI_DBGCAP", "n", allow_invisible=True)                                                                # seems debug?
        context.select_by_symbol("USB_HCD_TEST_MODE", "n", allow_invisible=True)                                                              # seems experiment?
        context.select_by_symbol("USB_ISIGHTFW", "n", allow_invisible=True)                                                                   # no firmware, we think it's rare
        context.select_by_symbol("USB_OTG_DISABLE_EXTERNAL_HUB", "n", allow_invisible=True)
        context.select_tree_by_symbol("USB_SUPPORT", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## other ############################################                                # FIXME: belongs here?
        context.select_tree_by_path(["Device Drivers", "Character devices"], "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: LED Support
        context.select_tree_by_symbol("NEW_LEDS", "m,y")

        # menu: Real Time Clock
        context.select_by_symbol("RTC_HCTOSYS", "n", allow_invisible=True)                                              # no service
        context.select_by_symbol("RTC_SYSTOHC", "n", allow_invisible=True)                                              # no service
        context.select_tree_by_symbol("RTC_CLASS", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: DMA Engine support
        context.select_tree_by_symbol("DMADEVICES", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: Multimedia support
        context.select_by_symbol("V4L_PLATFORM_DRIVERS", "n", allow_invisible=True)                                     # no rare
        context.select_tree_by_symbol("MEDIA_SUPPORT", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: X86 Platform Specific Device Drivers
        context.select_by_symbol("X86_PLATFORM_DEVICES", "y")
        context.select_by_symbol("ACPI_WMI", "m,y")

        ## other ############################################           # FIXME: belongs here?
        for sym_name, path_seq in context.list_symbols_in_tree_by_symbol("CRYPTO", with_path=True):
            if re.fullmatch("Userspace .*", path_seq[-1]) or re.fullmatch("User-space interface for .*", path_seq[-1]):
                context.select_by_symbol(sym_name, "m,y")
